{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=\"red\">注</font>: 使用 tensorboard 可视化需要安装 tensorflow (TensorBoard依赖于tensorflow库，可以任意安装tensorflow的gpu/cpu版本)\n",
    "\n",
    "```shell\n",
    "pip install tensorflow-cpu\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T07:41:29.893914Z",
     "start_time": "2025-01-20T07:41:27.487296Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:12:38.439425Z",
     "iopub.status.busy": "2025-01-21T06:12:38.439269Z",
     "iopub.status.idle": "2025-01-21T06:12:40.595298Z",
     "shell.execute_reply": "2025-01-21T06:12:40.594767Z",
     "shell.execute_reply.started": "2025-01-21T06:12:38.439403Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=10, micro=14, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cu124\n",
      "cuda:0\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据准备\n",
    "\n",
    "```shell\n",
    "$ tree -L 1 cifar-10                                    \n",
    "cifar-10\n",
    "├── sampleSubmission.csv\n",
    "├── test\n",
    "├── train\n",
    "└── trainLabels.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T06:12:40.596787Z",
     "iopub.status.busy": "2025-01-21T06:12:40.596464Z",
     "iopub.status.idle": "2025-01-21T06:12:41.429311Z",
     "shell.execute_reply": "2025-01-21T06:12:41.428573Z",
     "shell.execute_reply.started": "2025-01-21T06:12:40.596765Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "kaggle                            1.6.17\n",
      "\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.3.1\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "!pip list|grep kaggle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T06:12:41.430456Z",
     "iopub.status.busy": "2025-01-21T06:12:41.430075Z",
     "iopub.status.idle": "2025-01-21T06:12:42.103099Z",
     "shell.execute_reply": "2025-01-21T06:12:42.102472Z",
     "shell.execute_reply.started": "2025-01-21T06:12:41.430432Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "- path is now set to: /content\n"
     ]
    }
   ],
   "source": [
    "!mkdir -p ~/.kaggle\n",
    "!cp /mnt/workspace/kaggle.json ~/.kaggle/\n",
    "!chmod 600 ~/.kaggle/kaggle.json\n",
    "!kaggle config set -n path -v /content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:12:42.104397Z",
     "iopub.status.busy": "2025-01-21T06:12:42.103876Z",
     "iopub.status.idle": "2025-01-21T06:12:42.106942Z",
     "shell.execute_reply": "2025-01-21T06:12:42.106502Z",
     "shell.execute_reply.started": "2025-01-21T06:12:42.104371Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# !kaggle competitions download -c cifar-10 #下载比赛数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T06:12:42.107884Z",
     "iopub.status.busy": "2025-01-21T06:12:42.107558Z",
     "iopub.status.idle": "2025-01-21T06:12:42.222359Z",
     "shell.execute_reply": "2025-01-21T06:12:42.221754Z",
     "shell.execute_reply.started": "2025-01-21T06:12:42.107865Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cifar-10.zip\n"
     ]
    }
   ],
   "source": [
    "!ls /content/competitions/cifar-10/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T06:12:42.223492Z",
     "iopub.status.busy": "2025-01-21T06:12:42.223101Z",
     "iopub.status.idle": "2025-01-21T06:14:23.210121Z",
     "shell.execute_reply": "2025-01-21T06:14:23.209498Z",
     "shell.execute_reply.started": "2025-01-21T06:12:42.223463Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Archive:  /content/competitions/cifar-10/cifar-10.zip\n",
      "replace sampleSubmission.csv? [y]es, [n]o, [A]ll, [N]one, [r]ename: ^C\n"
     ]
    }
   ],
   "source": [
    "# !unzip /content/competitions/cifar-10/cifar-10.zip"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T06:14:23.213360Z",
     "iopub.status.busy": "2025-01-21T06:14:23.213020Z",
     "iopub.status.idle": "2025-01-21T06:14:23.328718Z",
     "shell.execute_reply": "2025-01-21T06:14:23.328117Z",
     "shell.execute_reply.started": "2025-01-21T06:14:23.213334Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "04_10_monkeys_model_1_aliyun.ipynb\t\t       runs\n",
      "05_10_monkeys_model_2_resnet50_finetune_2aliyun.ipynb  sampleSubmission.csv\n",
      "06_cifar10_model_2.ipynb\t\t\t       submission.csv\n",
      "checkpoints\t\t\t\t\t       test.7z\n",
      "competitions\t\t\t\t\t       train.7z\n",
      "kaggle.json\t\t\t\t\t       trainLabels.csv\n"
     ]
    }
   ],
   "source": [
    "!ls"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:25:01.810443Z",
     "iopub.status.busy": "2025-01-21T06:25:01.810099Z",
     "iopub.status.idle": "2025-01-21T06:25:17.965818Z",
     "shell.execute_reply": "2025-01-21T06:25:17.965269Z",
     "shell.execute_reply.started": "2025-01-21T06:25:01.810418Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# %pip install py7zr\n",
    "# import py7zr\n",
    "a =py7zr.SevenZipFile(r'./train.7z','r')\n",
    "a.extractall(path=r'./competitions/cifar-10/')\n",
    "a.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "ExecutionIndicator": {
     "show": false
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:17:43.607877Z",
     "iopub.status.busy": "2025-01-21T06:17:43.607512Z",
     "iopub.status.idle": "2025-01-21T06:19:24.394799Z",
     "shell.execute_reply": "2025-01-21T06:19:24.394257Z",
     "shell.execute_reply.started": "2025-01-21T06:17:43.607850Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# a =py7zr.SevenZipFile(r'./test.7z','r')\n",
    "# a.extractall(path=r'./competitions/cifar-10/')\n",
    "# a.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecutionIndicator": {
     "show": false
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:14:23.336424Z",
     "iopub.status.busy": "2025-01-21T06:14:23.336249Z",
     "iopub.status.idle": "2025-01-21T06:14:23.514781Z",
     "shell.execute_reply": "2025-01-21T06:14:23.514186Z",
     "shell.execute_reply.started": "2025-01-21T06:14:23.336405Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50000\n"
     ]
    }
   ],
   "source": [
    "!ls competitions/cifar-10/train/ |wc -l  #wc -l统计数量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:15:58.881339Z",
     "iopub.status.busy": "2025-01-21T06:15:58.880960Z",
     "iopub.status.idle": "2025-01-21T06:15:59.061075Z",
     "shell.execute_reply": "2025-01-21T06:15:59.060470Z",
     "shell.execute_reply.started": "2025-01-21T06:15:58.881312Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50000\n"
     ]
    }
   ],
   "source": [
    "!ls competitions/cifar-10/train/ |wc -l "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T07:51:56.103713Z",
     "start_time": "2025-01-20T07:51:54.990355Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:14:24.530939Z",
     "iopub.status.busy": "2025-01-21T06:14:24.530441Z",
     "iopub.status.idle": "2025-01-21T06:14:26.807497Z",
     "shell.execute_reply": "2025-01-21T06:14:26.806950Z",
     "shell.execute_reply.started": "2025-01-21T06:14:24.530913Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(PosixPath('competitions/cifar-10/train/1.png'), 'frog'),\n",
      " (PosixPath('competitions/cifar-10/train/2.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/3.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/4.png'), 'deer'),\n",
      " (PosixPath('competitions/cifar-10/train/5.png'), 'automobile')]\n",
      "[(PosixPath('competitions/cifar-10/test/1.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/2.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/3.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/4.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/5.png'), 'cat')]\n",
      "50000 300000\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "DATA_DIR = Path(\".\")\n",
    "DATA_DIR1 =Path(\"competitions/cifar-10/\")\n",
    "train_lables_file = DATA_DIR / \"trainLabels.csv\"\n",
    "test_csv_file = DATA_DIR / \"sampleSubmission.csv\" #测试集模板csv文件\n",
    "train_folder = DATA_DIR1 / \"train\"\n",
    "test_folder = DATA_DIR1 / \"test\"\n",
    "\n",
    "#所有的类别\n",
    "class_names = [\n",
    "    'airplane',\n",
    "    'automobile',\n",
    "    'bird',\n",
    "    'cat',\n",
    "    'deer',\n",
    "    'dog',\n",
    "    'frog',\n",
    "    'horse',\n",
    "    'ship',\n",
    "    'truck',\n",
    "]\n",
    "\n",
    "def parse_csv_file(filepath, folder): #filepath:csv文件路径，folder:图片所在文件夹\n",
    "    \"\"\"Parses csv files into (filename(path), label) format\"\"\"\n",
    "    results = []\n",
    "    #读取所有行\n",
    "    with open(filepath, 'r') as f:\n",
    "#         lines = f.readlines()  为什么加[1:]，可以试这个\n",
    "        #第一行不需要，因为第一行是标题\n",
    "        lines = f.readlines()[1:] \n",
    "    for line in lines:#依次去取每一行\n",
    "        image_id, label_str = line.strip('\\n').split(',') #图片id 和标签分离\n",
    "        image_full_path = folder / f\"{image_id}.png\"\n",
    "        results.append((image_full_path, label_str)) #得到对应图片的路径和分类\n",
    "    return results\n",
    "\n",
    "#解析对应的文件夹\n",
    "train_labels_info = parse_csv_file(train_lables_file, train_folder)\n",
    "test_csv_info = parse_csv_file(test_csv_file, test_folder)\n",
    "#打印\n",
    "import pprint\n",
    "pprint.pprint(train_labels_info[0:5])\n",
    "pprint.pprint(test_csv_info[0:5])\n",
    "print(len(train_labels_info), len(test_csv_info))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T07:52:54.792839Z",
     "start_time": "2025-01-20T07:52:54.757327Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:14:26.808453Z",
     "iopub.status.busy": "2025-01-21T06:14:26.808115Z",
     "iopub.status.idle": "2025-01-21T06:14:26.864731Z",
     "shell.execute_reply": "2025-01-21T06:14:26.864245Z",
     "shell.execute_reply.started": "2025-01-21T06:14:26.808432Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                            filepath       class\n",
      "0  competitions/cifar-10/train/1.png        frog\n",
      "1  competitions/cifar-10/train/2.png       truck\n",
      "2  competitions/cifar-10/train/3.png       truck\n",
      "3  competitions/cifar-10/train/4.png        deer\n",
      "4  competitions/cifar-10/train/5.png  automobile\n",
      "                                filepath       class\n",
      "0  competitions/cifar-10/train/45001.png       horse\n",
      "1  competitions/cifar-10/train/45002.png  automobile\n",
      "2  competitions/cifar-10/train/45003.png        deer\n",
      "3  competitions/cifar-10/train/45004.png  automobile\n",
      "4  competitions/cifar-10/train/45005.png    airplane\n",
      "                           filepath class\n",
      "0  competitions/cifar-10/test/1.png   cat\n",
      "1  competitions/cifar-10/test/2.png   cat\n",
      "2  competitions/cifar-10/test/3.png   cat\n",
      "3  competitions/cifar-10/test/4.png   cat\n",
      "4  competitions/cifar-10/test/5.png   cat\n"
     ]
    }
   ],
   "source": [
    "# train_df = pd.DataFrame(train_labels_info)\n",
    "train_df = pd.DataFrame(train_labels_info[0:45000]) # 取前45000张图片作为训练集\n",
    "valid_df = pd.DataFrame(train_labels_info[45000:]) # 取后5000张图片作为验证集\n",
    "test_df = pd.DataFrame(test_csv_info)\n",
    "\n",
    "train_df.columns = ['filepath', 'class']\n",
    "valid_df.columns = ['filepath', 'class']\n",
    "test_df.columns = ['filepath', 'class']\n",
    "\n",
    "print(train_df.head())\n",
    "print(valid_df.head())\n",
    "print(test_df.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T08:08:23.507372Z",
     "start_time": "2025-01-20T08:08:22.474521Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:24:23.843033Z",
     "iopub.status.busy": "2025-01-21T06:24:23.842664Z",
     "iopub.status.idle": "2025-01-21T06:24:23.851405Z",
     "shell.execute_reply": "2025-01-21T06:24:23.850759Z",
     "shell.execute_reply.started": "2025-01-21T06:24:23.843007Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from PIL import Image\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "from torchvision import transforms\n",
    "\n",
    "class Cifar10Dataset(Dataset):\n",
    "    df_map = {\n",
    "        \"train\": train_df,\n",
    "        \"eval\": valid_df,\n",
    "        \"test\": test_df\n",
    "    }\n",
    "    label_to_idx = {label: idx for idx, label in enumerate(class_names)} # 类别映射为idx\n",
    "    idx_to_label = {idx: label for idx, label in enumerate(class_names)} # idx映射为类别,为了test测试集使用\n",
    "    def __init__(self, mode, transform=None):\n",
    "        self.df = self.df_map.get(mode, None) # 获取对应模式的df，不同字符串对应不同模式\n",
    "        if self.df is None:\n",
    "            raise ValueError(\"mode should be one of train, val, test, but got {}\".format(mode))\n",
    "        # assert self.df, \"df is None\"\n",
    "        self.transform = transform\n",
    "        \n",
    "    def __getitem__(self, index):\n",
    "        img_path, label = self.df.iloc[index] # 获取图片路径和标签\n",
    "        img = Image.open(img_path).convert('RGB')\n",
    "        # # img 转换为 channel first\n",
    "        # img = img.transpose((2, 0, 1))\n",
    "        # transform\n",
    "        img = self.transform(img) # 数据增强\n",
    "        # label 转换为 idx\n",
    "        label = self.label_to_idx[label]\n",
    "        return img, label\n",
    "    \n",
    "    def __len__(self):\n",
    "        return self.df.shape[0] # 返回df的行数,样本数\n",
    "    \n",
    "IMAGE_SIZE = 32\n",
    "mean, std = [0.4914, 0.4822, 0.4465], [0.247, 0.243, 0.261]\n",
    "\n",
    "transforms_train = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)), #缩放\n",
    "        # random rotation 40\n",
    "        transforms.RandomRotation(40), #随机旋转\n",
    "        # horizaontal flip\n",
    "        transforms.RandomHorizontalFlip(),  #随机水平翻转\n",
    "        transforms.ToTensor(), #转换为tensor\n",
    "        # transforms.Normalize(mean, std) #标准化\n",
    "    ]) #数据增强\n",
    "\n",
    "transforms_eval = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        transforms.ToTensor(),\n",
    "        # transforms.Normalize(mean, std)\n",
    "    ])\n",
    "# ToTensor还将图像的维度从[height, width, channels]转换为[channels, height, width]。\n",
    "train_ds = Cifar10Dataset(\"train\", transforms_train)\n",
    "eval_ds = Cifar10Dataset(\"eval\", transforms_eval)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:24:23.852761Z",
     "iopub.status.busy": "2025-01-21T06:24:23.852426Z",
     "iopub.status.idle": "2025-01-21T06:24:23.857691Z",
     "shell.execute_reply": "2025-01-21T06:24:23.857164Z",
     "shell.execute_reply.started": "2025-01-21T06:24:23.852741Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_ds[0][1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:24:23.858389Z",
     "iopub.status.busy": "2025-01-21T06:24:23.858218Z",
     "iopub.status.idle": "2025-01-21T06:24:23.863394Z",
     "shell.execute_reply": "2025-01-21T06:24:23.862892Z",
     "shell.execute_reply.started": "2025-01-21T06:24:23.858370Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "249.56079"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sample1=train_ds[0][0]\n",
    "np.sum(sample1[2].numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-24T12:26:52.532323800Z",
     "start_time": "2024-07-24T12:26:52.525328700Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-01-21T06:24:23.864862Z",
     "iopub.status.busy": "2025-01-21T06:24:23.864545Z",
     "iopub.status.idle": "2025-01-21T06:24:23.869040Z",
     "shell.execute_reply": "2025-01-21T06:24:23.868545Z",
     "shell.execute_reply.started": "2025-01-21T06:24:23.864842Z"
    },
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0: 'airplane', 1: 'automobile', 2: 'bird', 3: 'cat', 4: 'deer', 5: 'dog', 6: 'frog', 7: 'horse', 8: 'ship', 9: 'truck'}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'airplane': 0,\n",
       " 'automobile': 1,\n",
       " 'bird': 2,\n",
       " 'cat': 3,\n",
       " 'deer': 4,\n",
       " 'dog': 5,\n",
       " 'frog': 6,\n",
       " 'horse': 7,\n",
       " 'ship': 8,\n",
       " 'truck': 9}"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "print(train_ds.idx_to_label)  # 类别映射为idx\n",
    "train_ds.label_to_idx # idx映射为类别"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T08:08:29.255263Z",
     "start_time": "2025-01-20T08:08:29.252817Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:24:23.869773Z",
     "iopub.status.busy": "2025-01-21T06:24:23.869606Z",
     "iopub.status.idle": "2025-01-21T06:24:23.872906Z",
     "shell.execute_reply": "2025-01-21T06:24:23.872340Z",
     "shell.execute_reply.started": "2025-01-21T06:24:23.869755Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "batch_size = 64\n",
    "train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True)   \n",
    "eval_dl = DataLoader(eval_ds, batch_size=batch_size, shuffle=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T06:24:23.873550Z",
     "iopub.status.busy": "2025-01-21T06:24:23.873382Z",
     "iopub.status.idle": "2025-01-21T06:24:24.009644Z",
     "shell.execute_reply": "2025-01-21T06:24:24.009013Z",
     "shell.execute_reply.started": "2025-01-21T06:24:23.873531Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-rw-r--r-- 1 root root 0  1月 21 14:07 /mnt/workspace/competitions/cifar-10/train/17742.png\n"
     ]
    }
   ],
   "source": [
    "!ls -l /mnt/workspace/competitions/cifar-10/train/17742.png"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:19.037012Z",
     "iopub.status.busy": "2025-01-21T06:31:19.036671Z",
     "iopub.status.idle": "2025-01-21T06:31:39.016994Z",
     "shell.execute_reply": "2025-01-21T06:31:39.016495Z",
     "shell.execute_reply.started": "2025-01-21T06:31:19.036989Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(tensor([0.4370, 0.4269, 0.3948]), tensor([0.2463, 0.2418, 0.2358]))\n"
     ]
    }
   ],
   "source": [
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds:\n",
    "        mean += img.mean(dim=(1, 2))\n",
    "        std += img.std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "# 经过 normalize 后 均值为0，方差为1\n",
    "print(cal_mean_std(train_ds))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:49:56.579457400Z",
     "start_time": "2024-07-23T06:49:56.499468400Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:47.581763Z",
     "iopub.status.busy": "2025-01-21T06:31:47.581415Z",
     "iopub.status.idle": "2025-01-21T06:31:47.653946Z",
     "shell.execute_reply": "2025-01-21T06:31:47.653244Z",
     "shell.execute_reply.started": "2025-01-21T06:31:47.581737Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "             model.0.weight             paramerters num: 3456\n",
      "              model.0.bias              paramerters num: 128\n",
      "             model.2.weight             paramerters num: 128\n",
      "              model.2.bias              paramerters num: 128\n",
      "             model.3.weight             paramerters num: 147456\n",
      "              model.3.bias              paramerters num: 128\n",
      "             model.5.weight             paramerters num: 128\n",
      "              model.5.bias              paramerters num: 128\n",
      "             model.7.weight             paramerters num: 294912\n",
      "              model.7.bias              paramerters num: 256\n",
      "             model.9.weight             paramerters num: 256\n",
      "              model.9.bias              paramerters num: 256\n",
      "            model.10.weight             paramerters num: 589824\n",
      "             model.10.bias              paramerters num: 256\n",
      "            model.12.weight             paramerters num: 256\n",
      "             model.12.bias              paramerters num: 256\n",
      "            model.14.weight             paramerters num: 1179648\n",
      "             model.14.bias              paramerters num: 512\n",
      "            model.16.weight             paramerters num: 512\n",
      "             model.16.bias              paramerters num: 512\n",
      "            model.17.weight             paramerters num: 2359296\n",
      "             model.17.bias              paramerters num: 512\n",
      "            model.19.weight             paramerters num: 512\n",
      "             model.19.bias              paramerters num: 512\n",
      "            model.22.weight             paramerters num: 4194304\n",
      "             model.22.bias              paramerters num: 512\n",
      "            model.24.weight             paramerters num: 5120\n",
      "             model.24.bias              paramerters num: 10\n"
     ]
    }
   ],
   "source": [
    "class CNN(nn.Module):\n",
    "    def __init__(self, num_classes):\n",
    "        super().__init__()\n",
    "        self.model = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=3, out_channels=128, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(128),# 批标准化，在通道数做的归一化\n",
    "            nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=\"same\"), #输出尺寸（128，32，32）\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(128),\n",
    "            nn.MaxPool2d(kernel_size=2), #输出尺寸（128，16，16）\n",
    "            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(256),\n",
    "            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, padding=\"same\"), #输出尺寸（256，16，16）\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(256),\n",
    "            nn.MaxPool2d(kernel_size=2),#输出尺寸（256，8，8）\n",
    "            nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(512),\n",
    "            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=\"same\"), #输出尺寸（512，8，8）\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(512),\n",
    "            nn.MaxPool2d(kernel_size=2), #输出尺寸（512，4，4）\n",
    "            nn.Flatten(), #展平\n",
    "            nn.Linear(8192, 512),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(512, num_classes),\n",
    "        ) #Sequential自动连接各层，把各层的输出作为下一层的输入\n",
    "        \n",
    "    def forward(self, x):\n",
    "        return self.model(x)\n",
    "        \n",
    "for key, value in CNN(len(class_names)).named_parameters():\n",
    "    print(f\"{key:^40}paramerters num: {np.prod(value.shape)}\")\n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:49:59.043655400Z",
     "start_time": "2024-07-23T06:49:58.976307900Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:47.655332Z",
     "iopub.status.busy": "2025-01-21T06:31:47.654951Z",
     "iopub.status.idle": "2025-01-21T06:31:47.719428Z",
     "shell.execute_reply": "2025-01-21T06:31:47.718789Z",
     "shell.execute_reply.started": "2025-01-21T06:31:47.655311Z"
    },
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total trainable parameters: 8779914\n"
     ]
    }
   ],
   "source": [
    "total_params = sum(p.numel() for p in CNN(len(class_names)).parameters() if p.requires_grad)\n",
    "print(f\"Total trainable parameters: {total_params}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:49:14.250276700Z",
     "start_time": "2024-07-23T06:49:14.242278700Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:47.720262Z",
     "iopub.status.busy": "2025-01-21T06:31:47.720007Z",
     "iopub.status.idle": "2025-01-21T06:31:47.724228Z",
     "shell.execute_reply": "2025-01-21T06:31:47.723637Z",
     "shell.execute_reply.started": "2025-01-21T06:31:47.720241Z"
    },
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8192"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "512*4*4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T08:18:30.878296Z",
     "start_time": "2025-01-20T08:18:30.872291Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:47.725131Z",
     "iopub.status.busy": "2025-01-21T06:31:47.724833Z",
     "iopub.status.idle": "2025-01-21T06:31:47.736412Z",
     "shell.execute_reply": "2025-01-21T06:31:47.735868Z",
     "shell.execute_reply.started": "2025-01-21T06:31:47.725111Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([32, 3, 64, 64])"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "input_4d = torch.randn(32, 3, 64, 64)  # 32 个样本，3 个通道，图像大小为 64x64\n",
    "bn2d = nn.BatchNorm2d(3)              # 对 3 个通道进行归一化\n",
    "output_4d = bn2d(input_4d)\n",
    "output_4d.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T08:18:32.896417Z",
     "start_time": "2025-01-20T08:18:32.888443Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:47.738289Z",
     "iopub.status.busy": "2025-01-21T06:31:47.737895Z",
     "iopub.status.idle": "2025-01-21T06:31:47.750707Z",
     "shell.execute_reply": "2025-01-21T06:31:47.750308Z",
     "shell.execute_reply.started": "2025-01-21T06:31:47.738269Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[-2.9528e-01,  9.2745e-02, -8.4144e-01,  ...,  2.7265e+00,\n",
       "           -3.4338e-01, -1.7162e-01],\n",
       "          [ 4.3799e-01, -3.7556e-01, -6.1130e-01,  ...,  3.9667e-01,\n",
       "            1.8735e+00, -1.4246e+00],\n",
       "          [-3.9025e-01, -1.0851e+00, -4.1067e-01,  ...,  1.1857e+00,\n",
       "           -2.2406e-01, -1.6071e+00],\n",
       "          ...,\n",
       "          [-1.2694e+00, -1.7684e+00, -8.0495e-01,  ..., -7.9863e-01,\n",
       "            4.2977e-01,  7.9318e-01],\n",
       "          [ 1.4989e+00,  1.2909e+00,  2.9786e-02,  ..., -1.3217e+00,\n",
       "           -1.4292e+00, -7.9237e-01],\n",
       "          [ 5.4842e-01, -8.0412e-01, -4.8811e-01,  ..., -3.1302e-01,\n",
       "            2.4343e-01,  8.9633e-01]],\n",
       "\n",
       "         [[ 6.5184e-01, -2.5157e-01,  1.0057e+00,  ...,  1.6892e-01,\n",
       "            2.5066e-01,  6.7026e-01],\n",
       "          [ 3.8122e-01, -1.3311e+00,  4.0030e-01,  ...,  1.3445e+00,\n",
       "            1.6509e-01, -5.8630e-01],\n",
       "          [ 9.1519e-01, -5.5596e-01,  4.8474e-01,  ..., -1.0135e+00,\n",
       "           -1.9426e+00,  3.8963e-01],\n",
       "          ...,\n",
       "          [-3.6697e-02, -2.9376e-01, -1.3162e+00,  ...,  7.2943e-01,\n",
       "           -1.5226e+00,  5.7554e-01],\n",
       "          [ 1.3595e+00, -1.6930e+00,  1.3717e+00,  ..., -9.5795e-01,\n",
       "            1.8966e+00, -6.7065e-01],\n",
       "          [-9.4686e-01, -5.3593e-02,  1.9739e-01,  ..., -2.6747e-01,\n",
       "            3.1294e+00,  7.3982e-01]],\n",
       "\n",
       "         [[ 2.7085e-01,  6.1659e-01, -1.3311e+00,  ..., -3.2131e-01,\n",
       "           -1.0651e-01, -3.1300e-01],\n",
       "          [-2.5183e-01,  2.3078e-01, -5.7591e-02,  ..., -1.8238e-01,\n",
       "           -7.6481e-01, -3.3343e-01],\n",
       "          [-1.0287e+00,  1.0769e-01,  9.5597e-01,  ..., -7.0450e-02,\n",
       "            7.4765e-01,  1.9605e+00],\n",
       "          ...,\n",
       "          [ 1.5067e+00, -1.2646e+00,  3.7143e-01,  ...,  2.7188e+00,\n",
       "            2.7301e+00,  9.0354e-01],\n",
       "          [ 6.8657e-01, -5.6745e-01, -4.6078e-01,  ..., -2.4023e-02,\n",
       "            8.7403e-01, -5.4791e-01],\n",
       "          [-1.8079e+00, -1.6478e+00,  2.4055e-01,  ...,  1.6868e+00,\n",
       "           -1.5718e+00,  7.8968e-01]]],\n",
       "\n",
       "\n",
       "        [[[-5.5528e-01,  8.3115e-01,  1.6282e+00,  ..., -4.0601e-01,\n",
       "           -2.3375e-01,  2.7143e+00],\n",
       "          [ 1.8864e+00, -1.4962e-01, -6.3747e-01,  ..., -1.5133e-01,\n",
       "            8.4965e-01,  5.8771e-01],\n",
       "          [ 3.7287e+00,  1.5037e-01,  5.7441e-01,  ...,  3.9061e-01,\n",
       "           -5.1600e-01, -1.4249e-01],\n",
       "          ...,\n",
       "          [ 1.4511e+00,  1.1423e+00, -9.4883e-01,  ...,  5.0151e-01,\n",
       "            2.5165e+00,  7.3804e-01],\n",
       "          [-1.2180e+00,  3.7592e-01,  3.7260e-01,  ...,  2.0730e-01,\n",
       "           -2.3668e-01, -3.9283e-01],\n",
       "          [ 5.7919e-01, -2.1793e-01, -1.1600e+00,  ..., -1.6312e-01,\n",
       "            1.2382e+00, -6.5450e-01]],\n",
       "\n",
       "         [[-1.6133e+00, -1.2477e-01,  4.4885e-01,  ..., -5.9066e-01,\n",
       "            1.4051e+00,  9.7000e-01],\n",
       "          [ 5.6278e-01,  2.8421e-01, -1.1009e+00,  ..., -1.7648e+00,\n",
       "            1.4607e+00, -5.1156e-02],\n",
       "          [ 7.3863e-01,  2.5900e-01,  6.4733e-01,  ..., -9.6137e-02,\n",
       "            9.1715e-01, -1.3840e+00],\n",
       "          ...,\n",
       "          [ 2.2508e-01, -3.7981e-01, -4.4224e-01,  ...,  4.5153e-01,\n",
       "           -1.7294e+00, -1.8412e-01],\n",
       "          [-8.1324e-01, -6.0887e-01,  1.0673e+00,  ..., -6.7775e-01,\n",
       "            1.2278e+00,  1.9316e+00],\n",
       "          [ 2.8200e+00,  1.0815e+00,  1.2596e+00,  ...,  2.0348e-01,\n",
       "            1.0752e+00, -1.0048e-01]],\n",
       "\n",
       "         [[-4.8374e-01, -3.8548e-01, -1.9594e-01,  ..., -6.1015e-01,\n",
       "            1.3421e+00,  1.3580e-01],\n",
       "          [ 1.0085e+00,  8.9045e-01,  3.5325e-01,  ..., -1.4399e+00,\n",
       "           -7.7245e-01,  1.2107e+00],\n",
       "          [-9.3357e-01,  8.9085e-01,  8.2362e-01,  ...,  2.0940e+00,\n",
       "            9.6857e-02,  9.5659e-01],\n",
       "          ...,\n",
       "          [-4.2852e-01, -1.3950e+00, -9.5607e-01,  ...,  1.3338e+00,\n",
       "            5.6002e-01, -8.2159e-01],\n",
       "          [-2.5524e-01, -8.0674e-01, -7.3024e-01,  ..., -8.2061e-01,\n",
       "           -2.2806e+00,  1.0878e+00],\n",
       "          [ 8.9050e-01,  5.2697e-01,  3.4511e-01,  ...,  2.0981e+00,\n",
       "            8.4081e-01,  4.4713e-01]]],\n",
       "\n",
       "\n",
       "        [[[ 2.7718e+00,  1.3064e+00, -1.0857e+00,  ..., -5.3938e-01,\n",
       "            9.5984e-01,  9.4237e-01],\n",
       "          [ 1.1149e+00,  3.0066e-01, -1.1226e+00,  ..., -3.7686e-01,\n",
       "            9.0218e-01, -6.7245e-01],\n",
       "          [-1.2982e+00,  9.6069e-01,  1.5933e+00,  ..., -3.9616e+00,\n",
       "            1.3313e+00, -4.0340e-01],\n",
       "          ...,\n",
       "          [ 1.1023e+00,  3.7655e-02, -1.5308e+00,  ..., -1.2868e-02,\n",
       "            5.2280e-01, -1.0084e+00],\n",
       "          [ 8.2740e-01,  7.0535e-01,  2.6402e-01,  ...,  3.9951e-01,\n",
       "            1.4918e-01, -2.9411e-01],\n",
       "          [ 3.1144e-01,  1.0250e+00, -7.2811e-02,  ..., -5.1261e-01,\n",
       "            1.0384e+00,  6.8247e-01]],\n",
       "\n",
       "         [[-2.5160e-01, -1.6179e+00,  7.3456e-01,  ..., -1.1901e-01,\n",
       "            6.6680e-02,  4.7719e-01],\n",
       "          [-1.8821e+00, -8.5477e-01, -1.3675e+00,  ..., -1.3166e+00,\n",
       "            3.4540e-01,  1.0651e-01],\n",
       "          [ 9.5001e-01, -1.2003e+00, -1.7006e+00,  ...,  6.2781e-01,\n",
       "            2.6412e-01,  4.2718e-01],\n",
       "          ...,\n",
       "          [ 1.5480e+00,  9.6609e-01,  3.1738e-01,  ...,  7.9278e-01,\n",
       "           -1.4468e+00, -2.4555e+00],\n",
       "          [-1.3022e-01, -6.1292e-01, -1.1420e-01,  ..., -7.4254e-01,\n",
       "            2.4646e-02, -1.3132e-01],\n",
       "          [ 8.8081e-01, -9.3414e-01,  1.9785e-02,  ...,  8.9204e-02,\n",
       "            1.0927e-01,  1.5355e+00]],\n",
       "\n",
       "         [[-3.0708e-01,  5.9278e-01, -3.7517e-01,  ...,  4.5061e-01,\n",
       "           -5.5919e-01,  1.2209e+00],\n",
       "          [ 6.7550e-02,  6.0524e-01, -2.0067e+00,  ...,  7.7179e-03,\n",
       "            1.9359e+00, -4.5052e-01],\n",
       "          [ 1.0490e+00, -4.0186e-01,  5.4376e-01,  ..., -5.5117e-02,\n",
       "            2.5513e-01,  6.7609e-01],\n",
       "          ...,\n",
       "          [ 2.8619e-01, -2.7011e-02,  6.0579e-01,  ..., -6.2159e-01,\n",
       "            8.7294e-01,  3.3139e-01],\n",
       "          [-6.7909e-01, -5.7689e-02, -1.4157e-01,  ...,  6.6165e-01,\n",
       "            3.3869e-01, -1.9106e+00],\n",
       "          [-3.5925e-01,  3.3815e-01,  7.9059e-01,  ...,  2.7083e-01,\n",
       "            2.9126e-01, -2.7860e+00]]],\n",
       "\n",
       "\n",
       "        ...,\n",
       "\n",
       "\n",
       "        [[[-1.0012e+00, -1.1448e+00,  1.2792e+00,  ..., -1.2840e+00,\n",
       "           -2.2073e-01,  2.3087e-01],\n",
       "          [-5.5333e-01, -5.8910e-02, -4.6614e-01,  ..., -1.2492e+00,\n",
       "            1.1605e-01, -1.0743e-01],\n",
       "          [-1.0006e+00,  9.1438e-01,  1.5246e-01,  ..., -3.9707e-01,\n",
       "            1.5844e+00,  8.2134e-01],\n",
       "          ...,\n",
       "          [-1.0144e+00,  7.7732e-01, -5.9369e-01,  ...,  6.3494e-01,\n",
       "            4.6851e-01,  7.2813e-01],\n",
       "          [-1.2272e-01, -3.9477e-01, -8.7977e-01,  ..., -2.3503e+00,\n",
       "            7.1767e-01,  8.1854e-01],\n",
       "          [-1.5876e+00,  2.1706e-01,  1.1969e+00,  ..., -7.9191e-02,\n",
       "           -1.5092e+00, -8.9101e-01]],\n",
       "\n",
       "         [[ 7.1410e-01, -9.8956e-01, -2.5200e-01,  ...,  1.8210e-01,\n",
       "           -2.0411e-01,  1.3379e+00],\n",
       "          [-1.2157e+00,  4.9736e-01, -4.6008e-02,  ...,  1.4666e+00,\n",
       "            1.2094e+00,  9.2394e-01],\n",
       "          [ 4.5420e-01,  1.3838e-01, -4.1074e-01,  ...,  1.8173e+00,\n",
       "            9.1241e-02, -1.8656e-01],\n",
       "          ...,\n",
       "          [-8.8547e-01,  1.8268e+00, -1.1652e+00,  ..., -1.5668e-01,\n",
       "           -1.0901e+00,  1.3211e+00],\n",
       "          [ 6.7070e-01,  8.3942e-02,  8.5718e-01,  ..., -1.4399e+00,\n",
       "            3.2283e-01, -1.6832e-03],\n",
       "          [-1.1730e+00, -1.3659e+00,  1.4837e+00,  ...,  5.7235e-02,\n",
       "            1.2437e+00, -2.5512e+00]],\n",
       "\n",
       "         [[ 8.4449e-01, -2.2219e-01,  2.5668e-01,  ...,  2.1875e+00,\n",
       "            1.7477e+00, -1.7780e+00],\n",
       "          [-6.6414e-01, -2.1455e+00,  4.6990e-01,  ..., -4.2676e-01,\n",
       "            2.1830e-01, -1.6195e+00],\n",
       "          [ 4.2390e-01, -7.2533e-01, -1.7149e+00,  ...,  9.6236e-01,\n",
       "           -1.3254e+00, -6.1890e-01],\n",
       "          ...,\n",
       "          [-7.6742e-01, -1.3627e+00, -1.0896e+00,  ...,  1.3716e-01,\n",
       "           -6.6287e-01,  5.2710e-01],\n",
       "          [ 4.9286e-01,  1.1453e+00,  8.9823e-01,  ..., -8.1865e-01,\n",
       "           -1.8335e+00, -1.4316e+00],\n",
       "          [-1.1737e+00,  8.2421e-01, -1.0431e+00,  ...,  3.3456e-01,\n",
       "            2.6534e+00,  2.9634e-01]]],\n",
       "\n",
       "\n",
       "        [[[ 4.3005e-02,  2.0450e-02, -4.9081e-01,  ..., -9.0836e-01,\n",
       "            6.4214e-02,  1.4652e+00],\n",
       "          [-4.4771e-01, -3.0467e-01, -3.5503e-01,  ..., -4.0486e-01,\n",
       "           -3.5568e-01, -1.3803e+00],\n",
       "          [-9.0436e-01, -2.2070e-01, -5.4000e-01,  ...,  1.3302e+00,\n",
       "           -1.2963e+00,  6.7843e-01],\n",
       "          ...,\n",
       "          [ 4.6574e-01, -9.1198e-01, -3.1775e-01,  ...,  4.7801e-01,\n",
       "            1.4124e+00, -4.1666e-01],\n",
       "          [ 1.0075e+00,  8.8420e-01,  2.0812e+00,  ..., -1.3168e+00,\n",
       "           -2.1020e+00,  6.3418e-01],\n",
       "          [ 1.3173e+00,  5.3392e-01, -1.4976e+00,  ...,  6.5610e-01,\n",
       "            3.9876e-01,  1.8145e+00]],\n",
       "\n",
       "         [[-2.5634e-01,  1.6331e+00, -9.9698e-01,  ..., -8.2281e-01,\n",
       "            1.0286e+00,  2.4943e-01],\n",
       "          [-4.2230e-01, -7.0706e-02,  6.3697e-01,  ...,  9.5391e-02,\n",
       "            7.0426e-01, -1.4533e+00],\n",
       "          [ 1.3905e+00,  4.1375e-01,  1.1925e+00,  ..., -8.0700e-01,\n",
       "           -8.1523e-01, -1.0387e+00],\n",
       "          ...,\n",
       "          [ 1.5092e+00, -1.0881e+00,  4.1606e-01,  ...,  9.0935e-01,\n",
       "           -3.9535e-01, -6.3558e-01],\n",
       "          [ 1.1775e+00,  7.1556e-01, -1.4750e-01,  ..., -3.8704e-01,\n",
       "            1.5245e+00,  1.7649e-01],\n",
       "          [-4.4865e-01,  3.1846e-01, -1.2177e+00,  ...,  1.2185e+00,\n",
       "            1.6686e+00, -7.1223e-02]],\n",
       "\n",
       "         [[-2.1732e+00, -2.3435e-01, -2.7294e-01,  ..., -5.4436e-01,\n",
       "           -1.2125e-01, -8.5058e-01],\n",
       "          [-9.7041e-01, -3.4727e-01, -1.0672e+00,  ..., -6.4885e-03,\n",
       "           -1.3578e+00,  1.0204e+00],\n",
       "          [-1.5419e+00, -2.2515e-01, -6.5146e-01,  ..., -7.4434e-01,\n",
       "           -1.1282e+00, -2.1227e+00],\n",
       "          ...,\n",
       "          [ 1.0812e+00,  4.4991e-01,  9.5094e-01,  ...,  9.1965e-01,\n",
       "            6.7751e-02, -7.6098e-01],\n",
       "          [ 1.5066e+00,  9.0610e-01,  6.3776e-01,  ...,  1.0437e-01,\n",
       "            7.5049e-01,  9.5470e-01],\n",
       "          [ 1.3590e+00, -4.8944e-01, -6.1998e-01,  ...,  5.5918e-02,\n",
       "            1.3969e+00, -2.4001e-01]]],\n",
       "\n",
       "\n",
       "        [[[-4.1670e-01,  6.3135e-01, -1.3858e+00,  ...,  3.1776e+00,\n",
       "            1.7799e+00,  6.8232e-02],\n",
       "          [ 2.5433e-01, -2.4889e-01, -3.5913e-02,  ...,  1.1035e-01,\n",
       "            1.6973e-02,  1.9438e+00],\n",
       "          [-8.9012e-01,  1.6466e-01,  4.4253e-01,  ...,  7.0122e-01,\n",
       "            7.9873e-01, -1.2062e-02],\n",
       "          ...,\n",
       "          [ 4.7383e-01,  1.1304e-01, -4.8601e-01,  ..., -1.6628e-01,\n",
       "            4.7723e-01,  1.5116e+00],\n",
       "          [-1.0257e+00,  4.4635e-02,  3.9693e-01,  ...,  4.7391e-01,\n",
       "            1.4416e+00,  8.2807e-01],\n",
       "          [ 1.7814e+00,  1.1830e+00, -1.1545e-01,  ...,  1.9083e+00,\n",
       "           -1.3854e-01,  2.0294e+00]],\n",
       "\n",
       "         [[ 5.3826e-01, -3.8383e-02,  4.7527e-01,  ...,  2.0777e+00,\n",
       "           -3.4410e-01,  2.3635e-01],\n",
       "          [-1.1504e+00, -4.4260e-01, -2.2292e+00,  ..., -8.4946e-01,\n",
       "           -2.1638e+00, -1.1942e-01],\n",
       "          [ 8.6656e-01, -1.1026e-01, -1.2234e+00,  ...,  4.7125e-01,\n",
       "           -1.4484e+00,  5.5583e-01],\n",
       "          ...,\n",
       "          [-1.6729e-01,  4.2142e-01, -1.0280e+00,  ..., -3.2075e+00,\n",
       "           -7.1290e-01, -9.7234e-01],\n",
       "          [ 1.0044e+00,  5.0936e-01,  2.2218e+00,  ..., -7.1238e-01,\n",
       "            5.3782e-01,  1.2246e+00],\n",
       "          [-1.1666e+00,  1.4855e-01, -1.6842e+00,  ..., -1.2250e+00,\n",
       "            9.3106e-01, -3.7167e-01]],\n",
       "\n",
       "         [[ 9.0770e-01, -1.4412e+00,  1.9708e+00,  ..., -6.2920e-01,\n",
       "           -4.6959e-01,  9.6737e-01],\n",
       "          [ 3.0198e-01, -1.0247e+00, -1.4179e+00,  ...,  2.1418e+00,\n",
       "            2.2656e-01, -2.2009e-02],\n",
       "          [-6.1453e-01,  2.6304e-01, -1.1581e+00,  ..., -3.4378e-01,\n",
       "           -5.9872e-01, -1.1786e+00],\n",
       "          ...,\n",
       "          [-3.1110e-01, -4.0518e-01,  3.5855e-01,  ...,  8.4623e-01,\n",
       "           -4.6145e-01,  3.2252e-01],\n",
       "          [-8.1209e-01, -9.8080e-02, -7.9272e-01,  ..., -1.4787e+00,\n",
       "            5.4071e-02,  2.5189e-01],\n",
       "          [-8.8840e-01, -5.1932e-01,  8.0507e-01,  ..., -6.0549e-01,\n",
       "            1.5941e-01,  4.1461e-02]]]], grad_fn=<NativeBatchNormBackward0>)"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output_4d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T08:18:50.605471Z",
     "start_time": "2025-01-20T08:18:50.598876Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:47.751550Z",
     "iopub.status.busy": "2025-01-21T06:31:47.751193Z",
     "iopub.status.idle": "2025-01-21T06:31:47.759373Z",
     "shell.execute_reply": "2025-01-21T06:31:47.758935Z",
     "shell.execute_reply.started": "2025-01-21T06:31:47.751531Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[-0.2953,  0.0927, -0.8414,  ...,  2.7265, -0.3434, -0.1716],\n",
       "          [ 0.4380, -0.3756, -0.6113,  ...,  0.3967,  1.8735, -1.4246],\n",
       "          [-0.3903, -1.0851, -0.4107,  ...,  1.1857, -0.2241, -1.6071],\n",
       "          ...,\n",
       "          [-1.2694, -1.7684, -0.8049,  ..., -0.7986,  0.4298,  0.7932],\n",
       "          [ 1.4989,  1.2909,  0.0298,  ..., -1.3217, -1.4292, -0.7924],\n",
       "          [ 0.5484, -0.8041, -0.4881,  ..., -0.3130,  0.2434,  0.8963]]],\n",
       "\n",
       "\n",
       "        [[[-0.5553,  0.8312,  1.6282,  ..., -0.4060, -0.2338,  2.7143],\n",
       "          [ 1.8864, -0.1496, -0.6375,  ..., -0.1513,  0.8496,  0.5877],\n",
       "          [ 3.7287,  0.1504,  0.5744,  ...,  0.3906, -0.5160, -0.1425],\n",
       "          ...,\n",
       "          [ 1.4511,  1.1423, -0.9488,  ...,  0.5015,  2.5165,  0.7380],\n",
       "          [-1.2180,  0.3759,  0.3726,  ...,  0.2073, -0.2367, -0.3928],\n",
       "          [ 0.5792, -0.2179, -1.1600,  ..., -0.1631,  1.2382, -0.6545]]],\n",
       "\n",
       "\n",
       "        [[[ 2.7718,  1.3064, -1.0857,  ..., -0.5394,  0.9598,  0.9424],\n",
       "          [ 1.1149,  0.3007, -1.1226,  ..., -0.3769,  0.9022, -0.6725],\n",
       "          [-1.2982,  0.9607,  1.5933,  ..., -3.9616,  1.3313, -0.4034],\n",
       "          ...,\n",
       "          [ 1.1023,  0.0377, -1.5308,  ..., -0.0129,  0.5228, -1.0084],\n",
       "          [ 0.8274,  0.7053,  0.2640,  ...,  0.3995,  0.1492, -0.2941],\n",
       "          [ 0.3114,  1.0250, -0.0728,  ..., -0.5126,  1.0384,  0.6825]]],\n",
       "\n",
       "\n",
       "        ...,\n",
       "\n",
       "\n",
       "        [[[-1.0012, -1.1448,  1.2792,  ..., -1.2840, -0.2207,  0.2309],\n",
       "          [-0.5533, -0.0589, -0.4661,  ..., -1.2492,  0.1161, -0.1074],\n",
       "          [-1.0006,  0.9144,  0.1525,  ..., -0.3971,  1.5844,  0.8213],\n",
       "          ...,\n",
       "          [-1.0144,  0.7773, -0.5937,  ...,  0.6349,  0.4685,  0.7281],\n",
       "          [-0.1227, -0.3948, -0.8798,  ..., -2.3503,  0.7177,  0.8185],\n",
       "          [-1.5876,  0.2171,  1.1969,  ..., -0.0792, -1.5092, -0.8910]]],\n",
       "\n",
       "\n",
       "        [[[ 0.0430,  0.0204, -0.4908,  ..., -0.9084,  0.0642,  1.4652],\n",
       "          [-0.4477, -0.3047, -0.3550,  ..., -0.4049, -0.3557, -1.3803],\n",
       "          [-0.9044, -0.2207, -0.5400,  ...,  1.3302, -1.2963,  0.6784],\n",
       "          ...,\n",
       "          [ 0.4657, -0.9120, -0.3178,  ...,  0.4780,  1.4124, -0.4167],\n",
       "          [ 1.0075,  0.8842,  2.0812,  ..., -1.3168, -2.1020,  0.6342],\n",
       "          [ 1.3173,  0.5339, -1.4976,  ...,  0.6561,  0.3988,  1.8145]]],\n",
       "\n",
       "\n",
       "        [[[-0.4167,  0.6314, -1.3858,  ...,  3.1776,  1.7799,  0.0682],\n",
       "          [ 0.2543, -0.2489, -0.0359,  ...,  0.1104,  0.0170,  1.9438],\n",
       "          [-0.8901,  0.1647,  0.4425,  ...,  0.7012,  0.7987, -0.0121],\n",
       "          ...,\n",
       "          [ 0.4738,  0.1130, -0.4860,  ..., -0.1663,  0.4772,  1.5116],\n",
       "          [-1.0257,  0.0446,  0.3969,  ...,  0.4739,  1.4416,  0.8281],\n",
       "          [ 1.7814,  1.1830, -0.1155,  ...,  1.9083, -0.1385,  2.0294]]]],\n",
       "       grad_fn=<NativeBatchNormBackward0>)"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bn2d = nn.BatchNorm2d(1)              # 对 3 个通道进行归一化\n",
    "output_4d1 = bn2d(input_4d[:, 0:1, :, :])\n",
    "output_4d1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练\n",
    "\n",
    "pytorch的训练需要自行实现，包括\n",
    "1. 定义损失函数\n",
    "2. 定义优化器\n",
    "3. 定义训练步\n",
    "4. 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:51:47.038660500Z",
     "start_time": "2024-07-23T06:51:46.193237300Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:47.760187Z",
     "iopub.status.busy": "2025-01-21T06:31:47.759870Z",
     "iopub.status.idle": "2025-01-21T06:31:47.787174Z",
     "shell.execute_reply": "2025-01-21T06:31:47.786679Z",
     "shell.execute_reply.started": "2025-01-21T06:31:47.760161Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:51:50.921239100Z",
     "start_time": "2024-07-23T06:51:49.522951600Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:47.788108Z",
     "iopub.status.busy": "2025-01-21T06:31:47.787737Z",
     "iopub.status.idle": "2025-01-21T06:31:47.978913Z",
     "shell.execute_reply": "2025-01-21T06:31:47.978394Z",
     "shell.execute_reply.started": "2025-01-21T06:31:47.788085Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:51:52.029341200Z",
     "start_time": "2024-07-23T06:51:52.023343500Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:47.979940Z",
     "iopub.status.busy": "2025-01-21T06:31:47.979596Z",
     "iopub.status.idle": "2025-01-21T06:31:47.985222Z",
     "shell.execute_reply": "2025-01-21T06:31:47.984576Z",
     "shell.execute_reply.started": "2025-01-21T06:31:47.979918Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:51:56.837899700Z",
     "start_time": "2024-07-23T06:51:56.828651400Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:47.985957Z",
     "iopub.status.busy": "2025-01-21T06:31:47.985792Z",
     "iopub.status.idle": "2025-01-21T06:31:47.990405Z",
     "shell.execute_reply": "2025-01-21T06:31:47.989779Z",
     "shell.execute_reply.started": "2025-01-21T06:31:47.985939Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:47.991208Z",
     "iopub.status.busy": "2025-01-21T06:31:47.991016Z",
     "iopub.status.idle": "2025-01-21T06:31:48.133823Z",
     "shell.execute_reply": "2025-01-21T06:31:48.133206Z",
     "shell.execute_reply.started": "2025-01-21T06:31:47.991189Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1) #最大值的索引\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())     # 计算准确率\n",
    "                loss = loss.cpu().item() # 计算损失\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step # 记录每一步的损失和准确率\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 20\n",
    "\n",
    "model = CNN(num_classes=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用 adam\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.001)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "# tensorboard_callback = TensorBoardCallback(\"runs/cifar-10\")\n",
    "# tensorboard_callback.draw_model(model, [1, 3, IMAGE_SIZE, IMAGE_SIZE])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints/cifar-10\", save_step=len(train_dl), save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=5)\n",
    "\n",
    "model = model.to(device)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:48.134751Z",
     "iopub.status.busy": "2025-01-21T06:31:48.134484Z",
     "iopub.status.idle": "2025-01-21T06:31:48.137636Z",
     "shell.execute_reply": "2025-01-21T06:31:48.137023Z",
     "shell.execute_reply.started": "2025-01-21T06:31:48.134730Z"
    },
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# record = training(\n",
    "#     model,\n",
    "#     train_dl,\n",
    "#     eval_dl,\n",
    "#     epoch,\n",
    "#     loss_fct,\n",
    "#     optimizer,\n",
    "#     tensorboard_callback=tensorboard_callback,\n",
    "#     save_ckpt_callback=save_ckpt_callback,\n",
    "#     early_stop_callback=early_stop_callback,\n",
    "#     eval_step=len(train_dl)\n",
    "#     )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:48.138299Z",
     "iopub.status.busy": "2025-01-21T06:31:48.138128Z",
     "iopub.status.idle": "2025-01-21T06:31:48.143441Z",
     "shell.execute_reply": "2025-01-21T06:31:48.142787Z",
     "shell.execute_reply.started": "2025-01-21T06:31:48.138280Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        # axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        # axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "# plot_learning_curves(record, sample_step=10)  #横坐标是 steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:48.145640Z",
     "iopub.status.busy": "2025-01-21T06:31:48.145330Z",
     "iopub.status.idle": "2025-01-21T06:31:50.552198Z",
     "shell.execute_reply": "2025-01-21T06:31:50.551502Z",
     "shell.execute_reply.started": "2025-01-21T06:31:48.145621Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_647/4227932207.py:3: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
      "  model.load_state_dict(torch.load(\"checkpoints/cifar-10/best.ckpt\", map_location=\"cpu\"))\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.4649\n",
      "accuracy: 0.8638\n"
     ]
    }
   ],
   "source": [
    "# dataload for evaluating\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(\"checkpoints/cifar-10/best.ckpt\", map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, eval_dl, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 推理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "ExecutionIndicator": {
     "show": false
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:31:50.553037Z",
     "iopub.status.busy": "2025-01-21T06:31:50.552854Z",
     "iopub.status.idle": "2025-01-21T06:33:39.568331Z",
     "shell.execute_reply": "2025-01-21T06:33:39.567815Z",
     "shell.execute_reply.started": "2025-01-21T06:31:50.553016Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 4688/4688 [01:49<00:00, 43.01it/s]\n"
     ]
    }
   ],
   "source": [
    "# test_df\n",
    "test_ds = Cifar10Dataset(\"test\", transform=transforms_eval)\n",
    "test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False, drop_last=False)\n",
    "\n",
    "preds_collect = [] # 预测结果收集器\n",
    "model.eval()\n",
    "for data, fake_label in tqdm(test_dl):\n",
    "    data = data.to(device=device)\n",
    "    logits = model(data) #得到预测结果\n",
    "    preds = [test_ds.idx_to_label[idx] for idx in logits.argmax(axis=-1).cpu().tolist()] # 得到预测类别，idx_to_label是id到字符串类别的映射\n",
    "    preds_collect.extend(preds)\n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T06:33:39.569210Z",
     "iopub.status.busy": "2025-01-21T06:33:39.568944Z",
     "iopub.status.idle": "2025-01-21T06:33:39.576638Z",
     "shell.execute_reply": "2025-01-21T06:33:39.576180Z",
     "shell.execute_reply.started": "2025-01-21T06:33:39.569188Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>filepath</th>\n",
       "      <th>class</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>competitions/cifar-10/test/1.png</td>\n",
       "      <td>cat</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>competitions/cifar-10/test/2.png</td>\n",
       "      <td>cat</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>competitions/cifar-10/test/3.png</td>\n",
       "      <td>cat</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>competitions/cifar-10/test/4.png</td>\n",
       "      <td>cat</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>competitions/cifar-10/test/5.png</td>\n",
       "      <td>cat</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                           filepath class\n",
       "0  competitions/cifar-10/test/1.png   cat\n",
       "1  competitions/cifar-10/test/2.png   cat\n",
       "2  competitions/cifar-10/test/3.png   cat\n",
       "3  competitions/cifar-10/test/4.png   cat\n",
       "4  competitions/cifar-10/test/5.png   cat"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "test_df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {
    "ExecutionIndicator": {
     "show": false
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:38:18.712051Z",
     "iopub.status.busy": "2025-01-21T06:38:18.711697Z",
     "iopub.status.idle": "2025-01-21T06:38:18.773720Z",
     "shell.execute_reply": "2025-01-21T06:38:18.773117Z",
     "shell.execute_reply.started": "2025-01-21T06:38:18.712025Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>id</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>299995</th>\n",
       "      <td>299996</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>299996</th>\n",
       "      <td>299997</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>299997</th>\n",
       "      <td>299998</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>299998</th>\n",
       "      <td>299999</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>299999</th>\n",
       "      <td>300000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>300000 rows × 1 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "            id\n",
       "0            1\n",
       "1            2\n",
       "2            3\n",
       "3            4\n",
       "4            5\n",
       "...        ...\n",
       "299995  299996\n",
       "299996  299997\n",
       "299997  299998\n",
       "299998  299999\n",
       "299999  300000\n",
       "\n",
       "[300000 rows x 1 columns]"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "test_df1 = pd.DataFrame(list(range(1,3*10**5+1)), columns=[\"id\"])\n",
    "test_df1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T06:38:24.426638Z",
     "iopub.status.busy": "2025-01-21T06:38:24.426292Z",
     "iopub.status.idle": "2025-01-21T06:38:24.439589Z",
     "shell.execute_reply": "2025-01-21T06:38:24.439100Z",
     "shell.execute_reply.started": "2025-01-21T06:38:24.426614Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>id</th>\n",
       "      <th>label</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>automobile</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "      <td>ship</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>ship</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   id       label\n",
       "0   1    airplane\n",
       "1   2    airplane\n",
       "2   3  automobile\n",
       "3   4        ship\n",
       "4   5        ship"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "test_df1[\"label\"] = preds_collect # 增加预测类别列,比赛要求这一列是label\n",
    "test_df1.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T06:38:34.142494Z",
     "iopub.status.busy": "2025-01-21T06:38:34.142149Z",
     "iopub.status.idle": "2025-01-21T06:38:34.309581Z",
     "shell.execute_reply": "2025-01-21T06:38:34.309001Z",
     "shell.execute_reply.started": "2025-01-21T06:38:34.142471Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 导出 submission.csv\n",
    "test_df1.to_csv(\"submission.csv\", index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:33:39.984069Z",
     "iopub.status.busy": "2025-01-21T06:33:39.983821Z",
     "iopub.status.idle": "2025-01-21T06:33:40.127030Z",
     "shell.execute_reply": "2025-01-21T06:33:40.126397Z",
     "shell.execute_reply.started": "2025-01-21T06:33:39.984047Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cifar-10  monkeys-cnn-relu  monkeys-resnet50\n"
     ]
    }
   ],
   "source": [
    "!ls checkpoints/"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
